package com.kitty.mina.codec.xf.v6;


import com.kitty.mina.codec.CodecContext;
import com.kitty.mina.codec.IMessageEncoder;
import com.kitty.mina.codec.MinaEncoder;
import com.kitty.mina.codec.SerializerHelper;
import com.kitty.mina.codec.xf.StanUtils;
import com.kitty.mina.message.Message;
import com.kitty.mina.session.SessionProperties;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import lombok.extern.slf4j.Slf4j;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;

import com.kitty.mina.codec.xf.v6.EncryptTable;
/*import org.slf4j.Logger;
import org.slf4j.LoggerFactory;*/


/**
 * 2.6x 新版本encoder
 *
 * @author StanWind
 * @create 2020 - 05 - 21 12:15 AM
 */
@Slf4j
public class WDEncoder implements ProtocolEncoder {

    private static Set<Integer> FILTER_MAP = new HashSet<>();

    static {
        //选线包 不加密发送
        FILTER_MAP.add(0xB1F3);
        FILTER_MAP.add(0x3357);
    }

    @Override
    public void dispose(IoSession arg0) throws Exception {

    }

    @Override
    public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
        CodecContext context = (CodecContext) session.getAttribute(SessionProperties.CODEC_CONTEXT);
        if (context == null) {
            context = new CodecContext();
            session.setAttribute(SessionProperties.CODEC_CONTEXT, context);
        }
        IoBuffer buffer = writeMessage((Message) message);
        out.write(buffer);
    }

    private IoBuffer writeMessage(Message message) {
        //选线包不加密发送
        if (FILTER_MAP.contains(message.getModule())) {
            return MinaEncoder.writeMessage(message);
        } else {
            return encryptMsg(message);///暂时不加密
        }
    }

    public static IoBuffer encryptMsg(Message message) {
        // 加密tableIndex
        int tableIndex = ThreadLocalRandom.current().nextInt(EncryptTable.TABLE.length) + 1;
        int moduleId = message.getModule();
        IoBuffer buffer = IoBuffer.allocate(32).setAutoExpand(true).setAutoShrink(true);
        buffer.putUnsignedShort(0x4D5A);
        buffer.putUnsignedShort(tableIndex);
        buffer.putUnsignedInt(0);
        //写入具体消息的内容
        IMessageEncoder msgEncoder = SerializerHelper.getInstance().getEncoder();

        byte[] body = msgEncoder.writeMessageBody(message);
        // 包体长度 data + module
        int posi = body.length + 2;
        buffer.putShort((short) posi);

        byte p1 = (byte) EncryptTable.TABLE[tableIndex - 1] [(moduleId >> 8 ) & 0xff];
        byte p2 = (byte) EncryptTable.TABLE[tableIndex - 1] [moduleId & 0xff];
//        buffer.putUnsignedShort(moduleId);
        buffer.put(p1).put(p2);
        //encrypt bytes
        for (int i = 0; i < body.length; i++) {
            body[i] = (byte) EncryptTable.TABLE[tableIndex - 1][body[i] & 0xff];
        }

        buffer.put(body);
        buffer.flip();
        buffer.rewind();
//        log.error("moduleId=={}=={}=={}",message.getModule(),buffer.getHexDump(),body.length);
        return buffer;
    }

    public static void main(String[] args) {
        //无符号 加密key
        int tableIndex = 0x0001;//ThreadLocalRandom.current().nextInt(EncryptTable.TABLE.length) + 1;
        int moduleId = 0xFDD1;
        String bodyHex = "02D8FFE30001031605C90400010001007C00010404BAA3B9EA000603000019C6000B0300000B90001F02001D006D01020010020000002C020003001102000000280717EE0029078000000202001D0009020071000502001D000D020039000703000019C6000C0300000B9000030300000143000A0300000ABE000E0200C50008030000056E003F030000000900190300000000004102000E0032020000003302000000340200000035020000003602000000380200000039020000003A020000003B020000003C020000003D03000027830040030000000D003E0300000064004203000000640037030001E2A600560717EE006802005A006902002A006B02FFF7006C02002D006A02002D006E0200000105020000010F020000010602000000790300000000007A030000000001090300000000010A03000000000126010002CF030000000002D0030000000003200300000000032103000000000322020000032302000003240200000325020000032602000002D102000002D202000002D302000002D402000002D502000002D602000001370100010E030000000000CD061403660100036703000000000368030000000003A3010003A4030000000003A5030000000003A70100036F040000E802003200E902000200EB02000500EC02FFCF00ED020005013B020000013C020000013D020000013E020000013F0200000140020000014102000001420200000143020000014402000001450200000146020000014702000001480200000149020000037303000000000374030000000003750100037803000000000376010003770300000000037A020000037B020000037C01000BC803000000000BC903000000000BCC03000000000BCD03000000000BCA03000000000BCB03000000000398030000014303990300000ABE039A030000056E039B030000000901320409333639343737383932010B0404BAA3B9EA00B00404B9FEB9FE01D2030000000001D30300000000015B0100";
        //开始
        IoBuffer buffer = IoBuffer.allocate(32).setAutoExpand(true).setAutoShrink(true);
//        buffer.putUnsignedInt(1297743872);
        buffer.putUnsignedShort(0x4D5A);
        buffer.putUnsignedShort(tableIndex);
        buffer.putUnsignedInt(0);
        byte[] body = StanUtils.hexToByteArray(bodyHex);
        // 包体长度
        int posi = body.length + 2;
        log.error(String.format("0x%x", body.length) + "-------" + posi);
        buffer.putShort((short) posi);
        byte p1 = (byte) EncryptTable.TABLE[tableIndex - 1] [(moduleId >> 8 ) & 0xff];
        byte p2 = (byte) EncryptTable.TABLE[tableIndex - 1] [moduleId & 0xff];
//        buffer.putUnsignedShort(moduleId);
        buffer.put(p1).put(p2);
        //encrypt bytes
        for (int i = 0; i < body.length; i++) {
            body[i] = (byte) EncryptTable.TABLE[tableIndex - 1][body[i] & 0xff];
        }

        buffer.put(body);
        buffer.flip();
        buffer.rewind();

        //4D5AB27700000000006E26913FFF7FFCF7C57FFFF161A9391E1588FEE30FAA334A70A34E0B7FBC2D2664DC3E4300D14B339BCF4576C57FF24A66CA3E447BAF3AC6F4B021F7C47FFE083A7FFEF7C57FFEF7C5A2DED3127FFDF6F47BEAC2803EC7B5F43AC9C68048BCB2F74FCDB3F639C7F7DA7DFEBFC557F9EC9F
        //结束
        byte[] enres = StanUtils.ioBufferToByte(buffer);
        String enc = StanUtils.bytesToHex(enres);
        System.out.println("加密后: " + enc);
    }
}
